<template>
    <div class="tree-wrapper" :id="`${id}-tree-wrapper`">
        <svg class="svg" v-show="data"></svg>
        <NoData v-show="!data"></NoData>
    </div>
</template>
<script>
import * as d3 from "d3";
import { throttle } from '@/libs/utils.js'
import Popper from 'popper.js/dist/umd/popper.js'
import rootDefault from '@/assets/tree/root-default-1.png'
import rootHover from '@/assets/tree/root-hover-1.png'
import rootLight from '@/assets/tree/root-light-1.png'
import rootCheck from '@/assets/tree/root-check.png'
import nodeDefault from '@/assets/tree/node-default-1.png'
import nodeHover from '@/assets/tree/node-hover-1.png'
import nodeLight from '@/assets/tree/node-light-1.png'
import nodeCheck from '@/assets/tree/node-check.png'
export default {
    name:'SkillTree',
    components:{},
    props:{
        id:{ //id
            type:String,
            default:''
        },
        operateId:{ //操作框id
            type:String,
            default:''
        },
        data:{ //树形数据
            type:[Object,String],
            default:''
        },
        current:{ //当前默认选中的节点
          type:String,
          default:''
        },
        allowCopy:{//是否允许鼠标右键选中节点及其子节点
          type:Boolean,
          default:false
        },
        createIsRoot:{ //创建的节点是不是根节点，根节点不能直接选择叶子节点创建
          type:Boolean,
          default:false
        }
    },
    data(){
        let defaultColor= 'rgb(163,163,163)'; //灰
        let hoverColor = 'rgb(0,127,238)';//蓝
        let checkColor = 'orange';
        let strokeColor = 'rgb(254,254,254)';
        return {
            rootDefault,
            rootHover,
            rootLight,
            rootCheck,
            nodeDefault,
            nodeHover,
            nodeLight,
            nodeCheck,
            preData:'',
            duration: 250,
            scaleRange:[0.1,1],
            margin:{
                top: 10,
                right: 10,
                bottom: 10,
                left: 10
            },
            tree:{},
            root:{},
            rootNode:{
                default:{
                    fill:'rgba(255,255,255,0)',stroke:defaultColor
                },
                hover:{
                    fill:'rgba(255,255,255,0)',stroke:hoverColor
                },
                light:{
                    fill:hoverColor,stroke:hoverColor
                },
                check:{
                  fill:'rgba(255,255,255,0)',stroke:checkColor
                }
            },
            childNode:{
              default:{
                  fill:'rgba(255,255,255,0)',stroke:defaultColor
              },
              hover:{
                  fill:'rgba(255,255,255,0)',stroke:hoverColor
              },
              light:{
                  fill:hoverColor,stroke:hoverColor
              },
              check:{
                fill:'rgba(255,255,255,0)',stroke:checkColor
              }
            },
            textNode:{
              default:{
                  fill:defaultColor,stroke:strokeColor
              },
              hover:{
                  fill:hoverColor,stroke:strokeColor
              },
              check:{
                  fill:checkColor,stroke:strokeColor
              }
            },
            images:[
                { id: 'root_default', url: rootDefault},
                { id: 'root_hover', url: rootHover},
                { id: 'root_light', url: rootLight},
                { id: 'root_check', url: rootCheck},
                { id: 'node_default', url: nodeDefault},
                { id: 'node_hover', url: nodeHover},
                { id: 'node_light', url: nodeLight},
                { id: 'node_check', url: nodeCheck},
            ],
            nodeSize:[120,100],
            nodeWidth: 46, 
            nodeHeight: 46,
            imageWidth:23,
            imageHeight:23,
            defaultColor, //灰
            hoverColor, //蓝
            checkColor //橙
        }
    },
    watch:{
        data(val){
            this.initTree()
        }
    },
    mounted(){
        if(this.data){
            this.initTree()
        }
    },
    methods:{
        initTree(){
            d3.select(`#${this.id}-tree-wrapper svg`).remove();
            //宽高
            const width = this.$el.offsetWidth;
            let height = this.$el.offsetHeight;
            const treeLevel = this.getMaxLevel([this.data],'level',0);
            height = treeLevel * this.nodeSize[1] < height ? height : treeLevel * this.nodeSize[1];
            this.svgHeight =  height;
            this.svgWidth = width;
            // 处理数据
            this.root = d3.hierarchy(this.data); //生成带有层级属性的数据
            // console.log('查看插件生成的tree数据',this.root)
            // 后代节点数据
            this.root.descendants().forEach((d, i) => {
              d.id = i;
              d._children = d.children;  
            });
            //tree
            this.tree = d3.tree().size([width - 160,height])
            .nodeSize(this.nodeSize);
            // .separation((a,b) => {
            //   return a.parent == b.parent ? 1 : 2
            // });
            // 用来拖拽图以及扩大缩放
            const zoom = d3
            .zoom()
            .extent([[0,0],[this.svgWidth,this.svgHeight]])
            .scaleExtent([0.5, 2])
            .on("zoom", ({transform}) => {
                let g = d3.selectAll(`.${this.id}-moveGroup`);
                g.attr('transform',transform)
            });
            // svg
            const svg = d3.select(`#${this.id}-tree-wrapper`)
            .append('svg')
            .attr('width', width)
            .attr('height', height)
            .style("font", '14px sans-serif')
            .style("user-select", "none")
            .call(zoom)
            this.svg = svg;
            // 画一些图形，不在页面显示的
            const defs = this.svg.append('defs'); //插入defs
            this.drawArrow(defs); //箭头
            this.drawBgImage(defs); //节点背景板图片
            //线统一配置
            this.gLink = this.svg.append("g")
              .attr("fill", "none")
              .attr("stroke", "#a2a3a5")
              .attr("stroke-opacity",1)
              .attr("stroke-width", 1)
              .attr("class", `${this.id}-moveGroup`);
            // 节点统一配置
            this.gNode = this.svg.append("g")
              .attr("cursor", "pointer")
              .attr("pointer-events", "all")
              .attr("class", `${this.id}-moveGroup`);
            this.update(this.root);
        },
        update(source) {
            let _this = this;
            const treeCol = this.getMaxLevel([this.data],'col',0); //横向节点个数
            if(treeCol !== 1 || source.depth === 0){
              this.tree(this.root);//生成x,y
            }
            this.root.x0 = this.svgWidth / 2;
            this.root.y0 = 0;
            const nodes = this.root.descendants()//.reverse(); //返回所有节点的数据
            const links = this.root.links();
            let nodesPos = nodes.map(node => {
              return {
                id:node.id,
                x:node.x,
                y:node.y
              }
            });
            if(treeCol === 1 && source.depth === 0){ //如果树是一条直线，在第一次的时候处理一下数据
              nodes.forEach(node => {
                this.setDir(node,nodesPos); //重新处理节点位置
              })
            }
            let top = this.root;
            let bottom = this.root;
            let left = this.root;
            let right = this.root;
            this.root.eachBefore(node => {
              if (node.y < top.y) top = node;
              if (node.y > bottom.y) bottom = node;
              if (node.x < left.x) left = node;
              if (node.x > right.x) right = node;
            });
            //节点占据的宽高
            let treeNodeWidth = right.x - left.x + this.nodeWidth + this.margin.left + this.margin.right
            let treeNodeHeight = bottom.y - top.y + this.nodeHeight +  this.margin.top + this.margin.bottom
            let svgWidth = this.svgWidth;
            let svgHeight = this.svgHeight;
            let viewBox = [];
            if(right.x - left.x + this.nodeWidth / 2 > svgWidth){ 
              let radio = (right.x - left.x + this.nodeSize[0] + 40) / svgWidth;
              let width = right.x - left.x + this.nodeSize[0] + 40;
              viewBox = [-width / 2,-this.nodeSize[1]/2,width,svgHeight*radio]
            }else{
              viewBox = [-svgWidth/2 + (right.x - Math.abs(left.x))/2 - this.nodeWidth / 2,-this.nodeSize[1]/2,svgWidth,svgHeight]
            }
            const transition = this.svg.transition()
              .duration(this.duration)
              .attr("viewBox", viewBox)
              .tween("resize", window.ResizeObserver ? null : () => () => this.svg.dispatch("toggle"));
            const drag = d3.drag().on('start',function(d) {
                console.log('dragstart',d)
              }).on('drag',function(d) {
                console.log('draging',d,d3.select(this))
                // 更新节点位置
                d.subject.x = d.x;
                d.subject.y = d.y;
                d3.select(this).attr("transform",`translate(${d.x},${d.y})`)
                // 更新连线位置
                // 获取所有相关连线
                let {id,label} = d.subject.data;
                let lines = [];
                d.subject.parent && lines.push({id:`#link-${id}-${label}`,type:'p',target:{x:d.x,y:d.y,depth:d.subject.depth},source:d.subject.parent})
                d.subject.children && d.subject.children.forEach(node => {
                  let {id,label} = node.data;
                  lines.push({
                    id:`#link-${id}-${label}`,
                    type:'c',
                    source:{x:d.x,y:d.y},
                    target:node
                  })
                })
                lines.forEach(line => {
                  d3.select(line.id)
                  .attr("d", d1 => {
                    let {source,target} = d1;
                    const s_dy = _this.nodeWidth / 2;
                    let sourceY = source.y + s_dy;
                    let targetY = target.y - (target.depth === 0 ? 0 : s_dy + 12);
                        console.log('新线',{
                        source: {...source,y:sourceY},
                        target: {...target,y:targetY}
                    })
                    return _this.diagonal({
                        source: {...source,y:sourceY},
                        target: {...target,y:targetY}
                    });
                })
                })
              }).on('end',function(d) {
                console.log('dragend',d)
              })
            // Update the nodes…
            const node = this.gNode.selectAll("g")
              .data(nodes, d => d.id);
            // 创建的节点先奔向之前source的位置
            const nodeEnter = node.enter().append("g")
              .attr("transform", d => `translate(${source.x0},${source.y0})`)
              .attr("fill-opacity", 0)
              .attr("stroke-opacity", 0)
              .attr('class', d => `${this.id}-node node-default`)
              .attr('id', d => `${this.id}-${d.data.id}`)
              .call(drag)
              .on("click", (e, d) => {
                if(d.children){
                  d._children = d.children;
                  d.children = null
                }else{
                  d.children = d._children;
                  d._children = null
                }
                // d._children = d.children ? null : d._children;
                // if (this.id == 'preTree') {
                //   this.options.cb();
                // }
                _this.update(d);
              })
              .on('mousedown',(data, source) => {
                if ( this.allowCopy && data.which == 3) { //鼠标右键选中节点
                  if (this.createIsRoot) {//如果是创建根节点，禁止选叶子节点
                    if (source.data.isLeafNode) {
                      // _this.options.alertMsg();
                      this.$Message.error('根节点不能复制叶子节点');
                      return;
                    }
                  }
                  this.$emit('node-mousedown',source)
                  // _this.options.cb(source);
                  if (_this.preData) { //把上一个节点的颜色还原
                    const childs = [];
                    _this.getAllChildren(_this.preData, childs);
                    _this.setCheck(childs,_this.preData,'default')
                    if (_this.preData.data.id == source.data.id) {
                      _this.preData = null;
                      this.$emit('node-mousedown',null)
                      return;
                    }
                  } 
                  //设置当前节点及子节点及连线的颜色
                  const ids = [];
                  _this.getAllChildren(source, ids)
                  _this.preData = {...source}; //存储
                  _this.setCheck(ids, source,'check') //设置颜色
                }
              })
              .on("mouseenter", (e, source) => {
                this.$emit('node-mouseenter',source)
                //改变样式
                this.setActive(source)
                //显示操作框
                if(this.operateId){
                    this.tooltip = new Popper(e.currentTarget, document.getElementById(this.operateId), {
                        placement: 'top'
                    });
                }
              })
              .on("mouseleave", (e,source) => {
                  let el = e.target;
                  let box = el.getBoundingClientRect();
                  if (!(e.y > box.top - 16 && e.y < box.top + 16)) {
                    this.tooltip.destroy();
                    this.tooltip ='';
                    this.$emit('node-mouseleave',source)
                  }
              })
            this.drawNode(nodeEnter)
            // 所有节点奔赴新的位置
            const nodeUpdate = node.merge(nodeEnter).transition()
              .attr("transform", d => `translate(${d.x},${d.y})`)
              .attr("fill-opacity", 1)
              .attr("stroke-opacity", 1);
              // transition
            // 消失的节点过度效果
            const nodeExit = node.exit().transition().remove()
              .attr("transform", d => `translate(${source.x},${source.y})`)
              .attr("fill-opacity", 0)
              .attr("stroke-opacity", 0);
        
            // Update the links…
            const link = this.gLink.selectAll("path")
              .data(links, d => d.target.id)
        
        
            // 新加入的线条线全部归并到source之前位置
            const linkEnter = link.enter().append("path")
              .attr("d", d => {
                  let { source, target } = d;
                  const s_dy = this.nodeWidth / 2;
                  let sourceY = source.y + s_dy;
                  let targetY = target.y - (target.depth === 0 ? 0 : s_dy + 12);
                  const o = {
                      x: source.x0,
                      y: source.y0
                  };
                  return this.diagonal({
                      source: o,
                      target: o
                  });
              })
              .attr('id', d => `link-${d.target.data.id}-${d.target.data.label}`)
              .attr('marker-end',d => this.getNodeArrow(d,'default')); //根据箭头标记的id号标记箭头
            // 所有的线出现在正确的位置上
            // transition
            link.merge(linkEnter).transition()
              .attr("d", (d) => {
                let { source, target } = d;
                const s_dy = this.nodeWidth / 2;
                if(d.target.dir === 'left-bottom'){
                  let sourceX = source.x - s_dy;
                  let targetY = target.y - (target.depth === 0 ? 0 : s_dy + 6);
                  return this.diagonal({
                      source: { ...source, x: sourceX },
                      target: { ...target, y: targetY }
                  });
                }else if(d.target.dir === 'right-top'){
                  let sourceX = source.x + s_dy;
                  let targetX = target.x - (s_dy + 6);
                  return this.diagonal({
                      source: { ...source, x: sourceX },
                      target: { ...target, x: targetX }
                  });
                }else{
                  let sourceY = source.y + s_dy + 2;
                  let targetY = target.y - (target.depth === 0 ? 0 : s_dy + 6);
                  return this.diagonal({
                      source: { ...source, y: sourceY },
                      target: { ...target, y: targetY }
                  });
                }
              });
        
            // 即将消失的线归并到source现在的位置上
            link.exit().transition().remove()
              .attr("d", d => {
                const o = {
                  x: source.x,
                  y: source.y
                };
                return this.diagonal({
                  source: o,
                  target: o
                });
              });
        
            //所有的点记录之前的位置
            this.root.eachBefore(d => {
              d.x0 = d.x;
              d.y0 = d.y;
            });
        },
        drawGrid(grid){

        },
        // 获取节点颜色色
        getNodeColor(d,nodeType,colorType){
            if(d.depth === 0){ //根节点
              return this.rootNode[nodeType][colorType]
            }else if(d.depth > 0){
              return this.childNode[nodeType][colorType]
            }
        },
        // 获取节点默认图片
        getNodeDefaultImg(d){
            if(d.depth === 0){ //根节点
                return rootDefault
            }else if(d.depth > 0){
                return nodeDefault
            }
        },
        // 获取节点图片
        getNodeImg(d,nodeType){
          // if(d.data.image){
          //   return `../Learn${d.data.image}`
          // }
          if(d.depth === 0){ //根节点
              return this[`root${nodeType.replace(nodeType[0],nodeType[0].toUpperCase())}`]
          }else if(d.depth > 0){
              return this[`node${nodeType.replace(nodeType[0],nodeType[0].toUpperCase())}`]
          }
        },
        getTextNodeColor(d,nodeType,colorType){
          return this['textNode'][nodeType][colorType]
        },
        getColor(type){ //默认和hover颜色
          return this[`${type}Color`];
        },
        getNodeArrow(d,nodeType){
          return `url(#${nodeType}-arrow)`
        },
        // 画箭头
        drawArrow(defs){
            let g = defs.append('g').attr('id','arrow');
            ['default','hover','check'].forEach(type => {
            g.append('marker')
            //.attr("id", function(d) { return d; })
            .attr('id', `${type}-arrow`)
            //.attr("markerUnits","strokeWidth")//设置为strokeWidth箭头会随着线的粗细发生变化
            .attr('markerUnits', 'userSpaceOnUse')
            .attr('viewBox', '0 -8 16 16') //坐标系的区域
            .attr('refX', 1) //箭头坐标
            .attr('refY', 0)
            .attr('markerWidth', 24) //标识的大小
            .attr('markerHeight', 24)
            .attr('orient', 'auto') //绘制方向，可设定为：auto（自动确认方向）和 角度值
            .attr('stroke-width', 1) //箭头宽度
            .append('path')
            .attr('d', 'M0,-4L4,0L0,4') //箭头的路径
            // .attr('d', 'M0,-4 L0,-8 L8,0 L0,8 L0,4') //箭头的路径
            .attr('fill', this[`${type}Color`]) //箭头颜色
            .attr('stroke', this[`${type}Color`]); //箭头颜色
            })
        },
        // 画背景图片
        drawBgImage(defs){
          let g = defs.append('g').attr('id', 'default-image')
          this.images.forEach(image => {
          g.append('image')
              .attr('id',image.id)
              .attr('width', this.imageWidth)
              .attr('height', this.imageHeight)
              .attr('xlink:href', image.url)
          })
        },
        // 获取树层级（横，树）
        getMaxLevel(treeData,type,maxLevel=0,maxCol=0){
            if(!treeData || !treeData.length) return maxLevel
            maxLevel ++;
            maxCol ++;
            (function getLevel(treeData,level=1,col=treeData.length){
              if(maxCol < col){
                maxCol = col
              }
              treeData.forEach(tree => {
                tree.level = level;
                if(maxLevel < level){
                  maxLevel = level;
                }
                if(tree.children && tree.children.length > 0){
                  getLevel(tree.children,level + 1,tree.children.length)
                }
              })
            })(treeData)
            return type === 'level' ?  maxLevel : maxCol;
        },
        drawNode(nodeEnter){
           let _this = this
            // 背景用颜色,画圆
            nodeEnter
                    .append('rect')
                    .attr('class','node-circle')
                    .attr('id',d => `bg-${d.data.id}`)
                    .attr('x', -(this.nodeWidth / 2))
                    .attr('y', -(this.nodeHeight / 2))
                    .attr('width', this.nodeWidth)
                    .attr('height', this.nodeHeight)
                    .attr('fill',d => _this.getNodeColor(d,'default','fill'))
                    .attr('rx', this.nodeHeight / 2)
                    .attr('stroke', d => _this.getNodeColor(d,'default','stroke'))//(d) => nodeColor(d.data)) //'rgb(246, 231, 111)'
                    .attr('stroke-width', 2);
            // 画可配置小图片
            const fogt = nodeEnter
            .append('foreignObject')
            .attr('x', -((this.nodeWidth - this.imageWidth) / 2))
            .attr('y', -((this.nodeHeight - this.imageHeight) / 2))
            .attr('width', this.imageWidth)
            .attr('height', this.imageHeight);
        fogt.append('xhtml:img')
            .attr('src', (d) => {
                let image_url = d.data.image;
                const node_image = image_url ? `../Learn${image_url}` : this.getNodeImg(d,'default');
                return node_image;
            })
            .attr('data-src',(d) => {
              if(d){
                return this.getNodeImg(d,'default')
              }
            })
            .attr('class','node-image')
            .attr('id', d => `node-image-${d.data.id}`)
            .attr('width', this.imageWidth)
            .attr('height',this.imageWidth)
            .on('error', (event,d) => {
                let img = event.srcElement;
                img.src = img.dataset.src;
                img.onerror = null;
            });
            // 画文本方框
            nodeEnter
            .append('rect')
            .attr('class',d => `node-text-box`)
            .attr('id',d => `node-text-box-${d.data.id}`)
            .attr('x', -(this.nodeWidth * 2) /2)
            .attr('y', this.nodeHeight / 2 - 5)
            .attr('width', this.nodeWidth * 2)
            .attr('height', 22)
            .attr('fill', d => _this.getTextNodeColor(d,'default','fill'))
            .style('stroke', d => _this.getTextNodeColor(d,'default','stroke'))
            .attr('stroke-width', 1)
            //画个数
            nodeEnter
            .append('text')
            .attr('dy', this.nodeHeight / 2 + 10)
            .attr('x',this.nodeWidth * 2 / 2 - 5)
            .attr('text-anchor','end')
            .text((d) => d.data.points ? d.data.points.length : 0)
            .style('fill','#fff')
            .style('font-size',12)
            // 画名称
            nodeEnter
            .append('text')
            .attr('dy', this.nodeHeight / 2 + 10)
            .attr('x', -(this.nodeWidth * 2) / 2 + 5)
            .attr('text-anchor', 'start')
            .text((d) => (d.data.name.length > 3 ? `${d.data.name.slice(0, 3)}...` : d.data.name))
            .style('fill','#fff')
            .style('font-size',12)
            nodeEnter.append("title")
            .text(d => d.data.name);
        },
        // 曲线函数
        diagonal(obj){
            let { source: s, target: d } = obj;
            let distanceY = d.y - s.y;
            let distanceX = d.x - s.x;
            let rx = 20; //椭圆的两个半轴的长度
            let ry = 20;
            let xAxisRotation = distanceX > 0 ? 90 : 270; //椭圆相对于坐标系的旋转角度，角度数而非弧度数
            let largeArcFlag = 0; //是标记绘制大弧(1)还是小弧(0)部分。
            let sweepFlag1 = distanceX < 0 ? 1 : 0; //是标记向顺时针(1)还是逆时针(0)方向绘制
            let sweepFlag2 = distanceX < 0 ? 0 : 1; //是标记向顺时针(1)还是逆时针(0)方向绘制
            let topHeight = 25;
            // if(!d.x){
            //   return
            // }
            if(s.x === d.x || s.y === d.y){ //画直线
              return `M ${s.x} ${s.y} L ${d.x} ${d.y}`
            }else if(d.x > s.x && d.y < s.y){ //目标在源的右上
              return `M ${s.x + this.nodeWidth / 2} ${s.y - this.nodeWidth / 2} 
                  L ${d.x + this.nodeWidth / 2 - rx} ${s.y - this.nodeWidth / 2}
                  L ${d.x + this.nodeWidth / 2 - rx} ${d.y - d.x}
                  A ${rx} ${ry}  ${xAxisRotation} ${largeArcFlag} ${sweepFlag1} ${d.x} ${s.y - this.nodeWidth / 2 + ry}
                  L ${d.x} ${d.y + this.nodeHeight + this.nodeHeight / 2 + 20}`
            }else if(d.dir === 'left-bottom'){
              let cdy = s.y + topHeight + ry;
              if(cdy > d.y){
                rx = ry = d.y - s.y - topHeight - 1
              }
              return `M ${s.x} ${s.y}
                  L ${distanceX > 0 ? d.x - rx : d.x + rx} ${s.y}
                  A ${rx} ${ry}  ${xAxisRotation} ${largeArcFlag} ${sweepFlag2} ${d.x} ${s.y +
                  ry}
                  L ${d.x} ${d.y}`;
            }else{
              let cdy = s.y + topHeight + ry;
              if(cdy > d.y){
                rx = ry = d.y - s.y - topHeight - 1
              }
              // console.log('路径',`M ${s.x} ${s.y}
              // L ${s.x} ${s.y + topHeight}
              // L ${distanceX > 0 ? d.x - rx : d.x + rx} ${s.y + topHeight}
              // A ${rx} ${ry}  ${xAxisRotation} ${largeArcFlag} ${sweepFlag2} ${d.x} ${s.y +
              // topHeight +
              // ry}
              // L ${d.x} ${d.y}`)
              return `M ${s.x} ${s.y}
                  L ${s.x} ${s.y + topHeight}
                  L ${distanceX > 0 ? d.x - rx : d.x + rx} ${s.y + topHeight}
                  A ${rx} ${ry}  ${xAxisRotation} ${largeArcFlag} ${sweepFlag2} ${d.x} ${s.y +
                  topHeight +
                  ry}
                  L ${d.x} ${d.y}`;
            }
        },
        // 获取所有子节点
        getAllChildren(d,childs = []){
            if(d.children){
                d.children.forEach(c => {
                  let {children,...info} = c;
                  childs.push(info)
                  this.getAllChildren(c,childs)
                })
            }
        },
        //是否允许移动
        allowMove(d,nodesPos,dir){
            let mx,my,condition;
            if(dir === 'left'){
                mx = -this.nodeSize[0];
                my = 0;
                condition = (node,tx,ty) => {
                    return node.x === tx && (node.y === ty || node.y === ty - this.nodeSize[1])
                }
            }else if(dir === 'right'){
                mx = this.nodeSize[0];
                my = -this.nodeSize[1];
                condition = (node,tx,ty) => {
                    return node.x === tx && node.y === ty
                }
            }
            let childs = []
            this.getAllChildren(d,childs);
            if(childs){
                childs.push(d);
                let allowMove = childs.every(cnode => {
                    let curPos = nodesPos.find(pos => pos.id === cnode.id);
                    let tx = curPos.x + mx;
                    let ty = curPos.y + my;
                    return nodesPos.filter((item) => {return condition(item,tx,ty)}).length == 0
                })
                return allowMove;
            }else{
                return true
            }
        },
        //移动节点
        setDir(d,nodesPos){
            let curPos = nodesPos.find(item => item.id === d.id);
            let curIndex = nodesPos.findIndex(item => item.id === d.id);
            let dir = '';
            let allowLeft = true;
            // 节点不是根节点，没有兄弟节点，且只有一个孩子节点，在现有位置上往左偏移一个节点位置（左下方）
            let canLeft = d.parent && d.parent.depth !== 0 && d.parent.children.length === 1 && (d.children && d.children.length === 1) && !d.dir && d.parent.dir != 'left-bottom' && d.parent.dir != 'right-top'
            if(canLeft && this.allowMove(d,nodesPos,'left')){
                //当前节点往左移动，当前节点的所有孩子节点都能往左偏移）
                d.x = curPos.x - this.nodeSize[0];
                d.y = curPos.y;
                nodesPos[curIndex].x = curPos.x - this.nodeSize[0];
                nodesPos[curIndex].y = curPos.y;
                d.dir = 'left-bottom';
                this.dealNodePos(d,nodesPos,d.dir); 
            }
            // 节点的父节点是left-bottom节点，且节点只有一个孩子节点 
            let canRight = d.parent && (d.parent.dir === 'left-bottom') && (!d.children || (d.children && d.children.length === 1));
            if(canRight && this.allowMove(d,nodesPos,'right')){
                //当前节点往右移动，当前节点的所有孩子节点都能往右偏移）
                d.x = curPos.x + this.nodeSize[0];
                d.y = curPos.y - this.nodeSize[1];
                nodesPos[curIndex].x = curPos.x + this.nodeSize[0];
                nodesPos[curIndex].y = curPos.y - this.nodeSize[1];
                d.dir = 'right-top';
                this.dealNodePos(d,nodesPos,d.dir); 
            }
        },
        //处理移动节点位置
        dealNodePos(node,nodesPos,dir){
            if(!node.children){
                return
            }
            let mx,my;
            if(dir === 'left-bottom'){
                mx = -this.nodeSize[0];
                my = 0;
            }else if(dir === 'right-top'){
                mx = this.nodeSize[0];
                my = -this.nodeSize[1];
            }
            node.children.forEach(cnode => {
                cnode.x = cnode.x + mx;
                cnode.y = cnode.y + my;
                let curIndex = nodesPos.findIndex(item => item.id === cnode.id);
                nodesPos[curIndex].x = cnode.x;
                nodesPos[curIndex].y = cnode.y;
                this.dealNodePos(cnode,nodesPos,dir)
            })
        },
        setActive(source){
          let _this = this;
          source.data.active = true;
          let nodes = d3.selectAll('.node-circle') //所有的圆节点
          let cur = d3.select(`#bg-${source.data.id}`)
          let nodeImgs = d3.selectAll('.node-image') //所有的图片节点
          let nodeImg = d3.select(`#node-image-${source.data.id}`)
          let nodeTexts = d3.selectAll('.node-text-box') //所有的文本rect
          let nodeText = d3.select(`#node-text-box-${source.data.id}`)
          // 背景用颜色
          //初始话
          nodes.attr('fill',d => _this.getNodeColor(d,d.data.check ? 'check':'default','fill')).attr('stroke',d => _this.getNodeColor(d,d.data.check ? 'check':'default','stroke'));
          nodeImgs.attr('src',d => _this.getNodeImg(d,d.data.check ? 'check' : 'default'));
          nodeTexts.attr('stroke',d => _this.getTextNodeColor(d,d.data.check ? 'check':'default','stroke')).attr('fill',d => _this.getTextNodeColor(d,d.data.check ? 'check':'default','fill'));
          //设置当前
          cur.attr('fill',_this.getNodeColor(source,source.data.check ? 'check':'hover','fill')).attr('stroke',_this.getNodeColor(source,source.data.check ? 'check':'hover','stroke'));
          nodeImg.attr('src',_this.getNodeImg(source,source.data.check ? 'check' : 'hover'));
          nodeText.attr('stroke',_this.getTextNodeColor(source,source.data.check ? 'check':'hover','stroke')).attr('fill',_this.getTextNodeColor(source,source.data.check ? 'check':'hover','fill'));
        },
        //右键选中设置样式
        setCheck(arr,source,nodeType) {
          const g = d3.select(`#${this.id}-${source.data.id}`);
          console.log('check',nodeType)
          this.checkIds = [];
          const node = d3.select(`#bg-${source.data.id}`);
          node.attr('stroke', this.getNodeColor(source,nodeType,'stroke')).attr('fill', this.getNodeColor(source,nodeType,'fill'))//null
          d3.select(`#node-text-box-${source.data.id}`).attr('fill', this.getTextNodeColor(source,nodeType,'stroke')).attr('fill', this.getTextNodeColor(source,nodeType,'fill'))//null
          d3.select(`#node-image-${source.data.id}`).attr('src',this.getNodeImg(source,nodeType))
          if(nodeType === 'check'){
            source.data.check = true;
          }else{
            source.data.check = false;
          }
          arr.forEach(item => {
            d3.select(`#bg-${item.data.id}`).attr('stroke', this.getNodeColor(item,nodeType,'stroke')).attr('fill', this.getNodeColor(item,nodeType,'fill'))//null
            d3.select(`#node-text-box-${item.data.id}`).attr('fill', this.getTextNodeColor(item,nodeType,'stroke')).attr('fill', this.getTextNodeColor(item,nodeType,'fill'))//null
            d3.select(`#node-image-${item.data.id}`).attr('src',this.getNodeImg(item,nodeType))
            // g.select('text').attr('stroke-width', setOption.twidth).attr('stroke', this.checkColor);
            // g.select('circle').attr('fill', nodeColor(item));
            if (item.data.id !== source.data.id) {
              d3.select(`#link-${item.data.id}-${item.data.label}`).attr('stroke', this.getNodeColor(item,nodeType,'stroke')).attr('marker-end', this.getNodeArrow(item,nodeType))
            }
            if(nodeType === 'check'){
              item.data.check = true;
            }else{
              item.data.check = false;
            }
          })
          if(nodeType === 'check'){
            this.checkIds = [source.data.id,...arr.map(data => data.data.id)];
          }
        },
        
    }
} 
</script>
<style scoped>
.tree-wrapper{
  height:100%;
}
.svg{
    width:100%;
    height:100%;
}
</style>